package com.goodwin.deity.desktop.util;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * Created by Sean Goodwin on 12/30/2014.
 */
public class CompareImages {

    private static final Dimension BASE_DIMENSION = new Dimension(300, 300);

    public static double compare(File file1, File file2) {
        double difference = 0.0;
        try {
            Color[][] sig1 = calcSignature(createThumbnail(ImageIO.read(file1), BASE_DIMENSION));
            Color[][] sig2 = calcSignature(createThumbnail(ImageIO.read(file2), BASE_DIMENSION));
            difference = calcDifference(sig1, sig2);
        } catch (IOException ie) {
            ie.printStackTrace();
        }

        return difference;
    }

    public static Color[][] calcSignature(BufferedImage image) {
        Color[][] sig = new Color[5][5];
        float[] prop = new float[]{1f / 10f, 3f / 10f, 5f / 10f, 7f / 10f, 9f / 10f};
        for (int x = 0; x < 5; x++) {
            for (int y = 0; y < 5; y++) {
                sig[x][y] = averageAround(createThumbnail(image, BASE_DIMENSION), prop[x], prop[y]);
            }
        }

        return sig;
    }

    public static double calcDifference(Color[][] sig1, Color[][] sig2) {
        double diff = 0;
        for (int x = 0; x < 5; x++) {
            for (int y = 0; y < 5; y++) {
                int r1 = sig1[x][y].getRed();
                int g1 = sig1[x][y].getGreen();
                int b1 = sig1[x][y].getBlue();
                int r2 = sig2[x][y].getRed();
                int g2 = sig2[x][y].getGreen();
                int b2 = sig2[x][y].getBlue();

                int red = r1 - r2;
                int green = g1 - g2;
                int blue = b1 - b2;

                diff += Math.sqrt(red * red + green * green + blue * blue);
            }
        }
        return diff;
    }

    private static Color averageAround(BufferedImage image, double px, double py) {
        double[] accumulate = new double[3];
        int sampleSize = 15;
        int numPixels = 0;
        int height = (int) Math.round(BASE_DIMENSION.getHeight());

        for (double x = px * height - sampleSize; x < px * height + sampleSize; x++) {
            for (double y = py * height - sampleSize; y < py * height + sampleSize; y++) {
                int rgb = image.getRGB((int) x, (int) y);
                accumulate[0] += (rgb >> 16) & 0xFF;
                accumulate[1] += (rgb >> 8) & 0xFF;
                accumulate[2] += (rgb & 0xFF);
                numPixels++;
            }
        }

        accumulate[0] /= numPixels;
        accumulate[1] /= numPixels;
        accumulate[2] /= numPixels;

        return new Color((int) accumulate[0], (int) accumulate[1], (int) accumulate[2]);
    }

    public static BufferedImage createThumbnail(BufferedImage source, double percent) {
        int width = (int) Math.round(source.getWidth() * percent);
        int height = (int) Math.round(source.getHeight() * percent);

        return createThumbnail(source, width, height);
    }

    public static BufferedImage createThumbnail(BufferedImage source, int width, int height) {
        return createThumbnail(source, new Dimension(width, height));
    }

    public static BufferedImage createThumbnail(BufferedImage source, Dimension dimension) {
        BufferedImage bi = getCompatibleImage((int) dimension.getWidth(), (int) dimension.getHeight());
        Graphics2D g2d = bi.createGraphics();
        double xScale = dimension.getWidth() / source.getWidth();
        double yScale = dimension.getHeight() / source.getHeight();
        AffineTransform at = AffineTransform.getScaleInstance(xScale, yScale);
        g2d.drawRenderedImage(source, at);
        g2d.dispose();

        return bi;
    }

    private static BufferedImage getCompatibleImage(int w, int h) {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();
        return gc.createCompatibleImage(w, h);
    }

    public static Dimension getScaledDimension(Dimension imgSize, Dimension boundary) {
        int original_width = imgSize.width;
        int original_height = imgSize.height;
        int bound_width = boundary.width;
        int bound_height = boundary.height;
        int new_width = original_width;
        int new_height = original_height;

        if (original_width > bound_width) {
            new_width = bound_width;
            new_height = (new_width * original_height) / original_width;
        }
        if (new_height > bound_height) {
            new_height = bound_height;
            new_width = (new_height * original_width) / original_height;
        }
        return new Dimension(new_width, new_height);
    }
}
